#pragma once

#include <iostream>
#include <string>
#include <cassert>
#include <ctime>
#include <utility>
#include <unordered_map>
#include <functional>
#include "Epoll.hpp"
#include "Sock.hpp"
#include "Err.hpp"
#include "log.hpp"
#include "Protocol.hpp"

using namespace Protocal_ns;

static const int gport = 8888;
static const int bsize = 1024;
static const int linkTimeout = 30;

class Connection;
class EpollServer;

using func_t = std::function<Response(const Request &)>;
using callback_t = std::function<void(Connection *)>;

class Connection
{
public:
    Connection(int fd, std::string ip, uint32_t port)
        : _fd(fd), _clientip(ip), _clientport(port)
    {
    }

    void Register(callback_t recver, callback_t sender, callback_t excepter)
    {
        _recver = recver;
        _sender = sender;
        _excepter = excepter;
    }

    ~Connection()
    {
    }

    // IO信息
    int _fd;
    std::string _inbuffer;
    std::string _outbuffer;

    // IO处理函数
    callback_t _recver;
    callback_t _sender;
    callback_t _excepter;

    // 用户信息
    std::string _clientip;
    uint32_t _clientport;

    uint32_t _event;

    // 回指指针
    EpollServer *R;

    time_t _lasttime; // 最后一次就绪的时间
};

class EpollServer
{
public:
    static const int gnum = 64;

    EpollServer(func_t func, uint16_t port = gport) : _func(func), _port(port)
    {
    }

    ~EpollServer()
    {
        _listhensock.Close();
        _epoller.Close();
    }

    void AddConnection(int fd, uint32_t event, std::string ip = "127.0.0.1", uint32_t port = gport)
    {
        // 设置fd为非阻塞
        if (event & EPOLLET)
            Util::SetNonBlock(fd);

        // 创建Connection
        Connection *conn = new Connection(fd, ip, port);
        if (fd == _listhensock.Fd())
        {
            conn->Register(std::bind(&EpollServer::Accepter, this, std::placeholders::_1), nullptr, nullptr);
        }
        else
        {
            conn->Register(std::bind(&EpollServer::Recever, this, std::placeholders::_1),
                           std::bind(&EpollServer::Sender, this, std::placeholders::_1),
                           std::bind(&EpollServer::Execpter, this, std::placeholders::_1));
        }

        conn->_event = event;
        conn->R = this;
        conn->_lasttime = time(nullptr);

        // 将其对应的Connection添加到_connections中
        _connections.insert(std::make_pair(fd, conn));

        // 将fd添加到epoll中
        bool r = _epoller.AMEvent(fd, event, EPOLL_CTL_ADD);
        assert(r);
        (void)r;

        logmessage(DEBUG, "add sucess,fd: %d,clientinfo[%s%d]", fd, ip.c_str(), port);
    }

    void InitServer()
    {
        _listhensock.Socket();
        _listhensock.Bind(_port);
        _listhensock.Listhen();
        _epoller.Create();

        AddConnection(_listhensock.Fd(), EPOLLIN | EPOLLET);
        logmessage(DEBUG, "init server succsess");
    }

    bool EnableReadWrite(Connection *conn, bool readable, bool writeable)
    {
        uint32_t event = (readable ? EPOLLIN : 0) | (writeable ? EPOLLOUT : 0) | EPOLLET;
        return _epoller.AMEvent(conn->_fd, event, EPOLL_CTL_MOD);
    }

    // 连接管理器
    void Accepter(Connection *conn)
    {
        do
        {
            int err = 0;
            std::string clientip;
            uint16_t clientport;
            int sock = _listhensock.Accept(&clientip, &clientport, &err);
            if (sock > 0)
            {
                logmessage(DEBUG, "[%s][%d]已连接", clientip.c_str(), clientport);
                AddConnection(sock, EPOLLIN | EPOLLET, clientip, clientport);
            }
            else
            {
                if (err == EAGAIN || err == EWOULDBLOCK)
                    break;
                else if (err == EINTR)
                    continue;
                else
                {
                    logmessage(WARNING, "errstring: %s, errcode: %d", strerror(errno), errno);
                    continue; // 就算有一个sock接收失败也要将所有的请求读完
                }
            }

        } while (conn->_event & EPOLLET);

        logmessage(DEBUG, "accepter done ...");
    }

    void HandlerRequest(Connection *conn)
    {
        int quit = false;
        while (!quit)
        {
            std::string requestStr;

            // 提取完整报文
            int n = ReadPackage(conn->_inbuffer, &requestStr);
            if (n > 0)
            {
                // 提取有效载荷
                requestStr = RemoveHeader(requestStr, n);
                Request req;

                // 反序列化
                req.DeSerialize(requestStr);

                // 业务处理
                Response resp = _func(req); // 一定是完整的报文

                // 序列化
                std::string Respstr;
                resp.Serialize(&Respstr);

                // 添加报头
                Respstr = AddHeader(Respstr);

                // 返回
                conn->_outbuffer += Respstr;
                conn->R->EnableReadWrite(conn, true, true);
            }
            else
                quit = true;
        }
    }

    bool RecverHelper(Connection *conn)
    {
        int ret = true;
        conn->_lasttime = time(nullptr); // 更新最近一次访问时间
        do
        {
            char buffer[bsize];
            ssize_t n = recv(conn->_fd, buffer, sizeof(buffer) - 1, 0);
            if (n > 0)
            {
                buffer[n] = '\0';
                conn->_inbuffer += buffer;
            }
            else if (n == 0)
            {
                conn->_excepter(conn);
                ret = false;
                break;
            }
            else
            {
                if (errno == EAGAIN || errno == EWOULDBLOCK)
                    break;

                else if (errno == EINTR)
                    continue;
                else
                {
                    conn->_excepter(conn);
                    ret = false;
                    break;
                }
            }
        } while (conn->_event & EPOLLET);
        // 已读完本轮数据
        return ret;
    }

    void Recever(Connection *conn)
    {
        if (!RecverHelper(conn))
            return;

        HandlerRequest(conn);

        // 面对写入情况，直接写入
        if (!conn->_outbuffer.empty())
            conn->_sender(conn);
    }

    void Sender(Connection *conn)
    {
        bool safe = true;
        do
        {
            ssize_t n = send(conn->_fd, conn->_outbuffer.c_str(), conn->_outbuffer.size(), 0);
            if (n > 0)
            {
                conn->_outbuffer.erase(0, n);
                if (conn->_outbuffer.empty())
                    break;
            }
            else
            {
                if (errno == EAGAIN || errno == EWOULDBLOCK)
                {
                    break;
                }
                else if (errno == EINTR)
                    continue;
                else
                {
                    safe = false;
                    conn->_excepter(conn);
                    break;
                }
            }
        } while (conn->_event & EPOLLET);

        if (!safe) // 避免出现错误后再调用函数
            return;

        if (!conn->_outbuffer.empty())
            EnableReadWrite(conn, true, true);
        else
            EnableReadWrite(conn, true, false);
    }

    void Execpter(Connection *conn)
    {
        logmessage(DEBUG, "Execpter... ,fd: %d,clientinfo[%s%d]", conn->_fd, conn->_clientip.c_str(), conn->_clientport);

        // 从epoll移除fd
        _epoller.DelEvent(conn->_fd);

        // 关闭fd
        close(conn->_fd);

        // 释放conn对象
        delete conn;

        // 在容器中移除
        _connections.erase(conn->_fd);
    }

    void checklink()
    {
        time_t cur = time(nullptr);
        for (auto &it : _connections)
        {
            if (it.second->_lasttime + linkTimeout < cur)
            {
                Execpter(it.second);
            }
            else
                continue;
        }
    }

    void Dispatcher()
    {
        int timeout = 1000;
        while (true)
        {
            LoopOnce(timeout);

            checklink();
        }
    }

    bool ConnIsExist(int fd)
    {
        return _connections.find(fd) != _connections.end();
    }

    void LoopOnce(int timeout)
    {
        int n = _epoller.Wait(_revs, gnum, timeout); // 获取就绪文件fd
        for (int i = 0; i < n; i++)
        {
            int fd = _revs[i].data.fd;
            uint32_t events = _revs[i].events;

            logmessage(DEBUG, "当前正在处理%d上的%s", fd, (events & EPOLLIN) ? "EPOLLIN" : "OTHER");

            if ((events & EPOLLERR) || (events & EPOLLHUP))
                events |= (EPOLLIN | EPOLLOUT);

            if ((events & EPOLLIN) && ConnIsExist(fd))
                _connections[fd]->_recver(_connections[fd]);
            if ((events & EPOLLOUT) && ConnIsExist(fd))
                _connections[fd]->_sender(_connections[fd]);
        }
    }

private:
    uint16_t _port;
    Sock _listhensock;
    Epoller _epoller;
    struct epoll_event _revs[gnum];
    func_t _func;

    std::unordered_map<int, Connection *> _connections;
};